package com.southminority.ethnic.until;

import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.southminority.ethnic.column.ExcelColumn;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Component;

import org.apache.poi.ss.usermodel.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;

@Slf4j
@Component
public class ExcelUntil {
    /**
     * 导出Excel（支持注解扩展属性）
     */
    public static <T> byte[] exportExcel(List<T> dataList) throws IOException, IllegalAccessException {
        if (dataList == null || dataList.isEmpty()) {
            throw new IllegalArgumentException("数据列表不能为空");
        }

        Class<?> clazz = dataList.get(0).getClass();
        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet("数据");
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        // 1. 获取并过滤字段（仅导出带有注解且非 hidden 的字段）
        List<Field> fields = getAllFields(clazz).stream()
                .filter(field -> {
                    ExcelColumn anno = field.getAnnotation(ExcelColumn.class);
                    return anno != null && !anno.hidden(); // 隐藏字段不参与导出
                })
                .collect(Collectors.toList());

        // 2. 创建样式（复用样式避免Excel文件过大）
        Map<String, CellStyle> styleMap = createStyles(workbook);
        CellStyle defaultStyle = styleMap.get("default");
        CellStyle nonNullWarningStyle = styleMap.get("nonNullWarning"); // 非空警告样式（黄色背景）

        // 3. 填充表头
        Row headerRow = sheet.createRow(0);
        for (int i = 0; i < fields.size(); i++) {
            Field field = fields.get(i);
            field.setAccessible(true);
            ExcelColumn anno = field.getAnnotation(ExcelColumn.class);

            // 表头名称
            String columnName = (anno != null && !anno.name().isEmpty()) ? anno.name() : field.getName();
            Cell cell = headerRow.createCell(i);
            cell.setCellValue(columnName);

            // 表头颜色（如果指定）
            if (anno != null && !anno.color().isEmpty()) {
                CellStyle colorStyle = styleMap.get(anno.color().toUpperCase());
                if (colorStyle != null) {
                    cell.setCellStyle(colorStyle);
                }
            } else {
                cell.setCellStyle(defaultStyle);
            }
        }

//        Cell tCell = headerRow.createCell(fields.size()+1);
//
//        tCell.setCellValue("tip：黄色背景表示必填项");
//        //tCell.setCellStyle(nonNullWarningStyle);
//        sheet.autoSizeColumn(fields.size()+1);

        // 4. 填充数据行
        for (int i = 0; i < dataList.size(); i++) {
            T data = dataList.get(i);
            Row dataRow = sheet.createRow(i + 1);

            for (int j = 0; j < fields.size(); j++) {
                Field field = fields.get(j);
                field.setAccessible(true);
                ExcelColumn anno = field.getAnnotation(ExcelColumn.class);
                Object value = field.get(data);
                Cell cell = dataRow.createCell(j);

                // 设置单元格值
                if (value != null) {
                    if (value instanceof String) {
                        cell.setCellValue((String) value);
                    } else if (value instanceof Number) {
                        cell.setCellValue(((Number) value).doubleValue());
                    } else if (value instanceof Date) {
                        cell.setCellValue(sdf.format((Date) value));
                    } else if (value instanceof Boolean) {
                        cell.setCellValue((Boolean) value);
                    } else {
                        cell.setCellValue(value.toString());
                    }
                }else {
                    cell.setCellValue("NULL");
                }

            }
        }

        // 5. 设置列宽（优先使用注解指定的宽度，否则自动调整）
        for (int i = 0; i < fields.size(); i++) {
            Field field = fields.get(i);
            ExcelColumn anno = field.getAnnotation(ExcelColumn.class);
            if (anno != null && anno.columnWidth() > 0) {
                // POI列宽单位是1/256个字符宽度，这里转换为用户输入的"字符数"
                sheet.setColumnWidth(i, anno.columnWidth() * 256);
            } else {
                sheet.autoSizeColumn(i); // 自动调整
            }
        }

        // 6. 写入并返回字节数组
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        workbook.write(outputStream);
        workbook.close();
        return outputStream.toByteArray();
    }

    /**
     * 创建通用样式（表头颜色、非空警告等）
     */
    private static Map<String, CellStyle> createStyles(Workbook workbook) {
        Map<String, CellStyle> styles = new HashMap<>();

        // 默认样式
        CellStyle defaultStyle = workbook.createCellStyle();
        defaultStyle.setBorderTop(BorderStyle.THIN);
        defaultStyle.setBorderBottom(BorderStyle.THIN);
        defaultStyle.setBorderLeft(BorderStyle.THIN);
        defaultStyle.setBorderRight(BorderStyle.THIN);
        styles.put("default", defaultStyle);

        // 非空警告样式（黄色背景）
        CellStyle nonNullWarningStyle = workbook.createCellStyle();
        nonNullWarningStyle.cloneStyleFrom(defaultStyle); // 复制默认样式的边框等属性
        nonNullWarningStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
        nonNullWarningStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); // 填充模式必须设置，否则背景色不生效
        styles.put("nonNullWarning", nonNullWarningStyle);

        // 支持POI预定义颜色（如"RED"、"GREEN"等）
        for (IndexedColors color : IndexedColors.values()) {
            CellStyle colorStyle = workbook.createCellStyle();
            colorStyle.cloneStyleFrom(defaultStyle);
            colorStyle.setFillForegroundColor(color.getIndex());
            colorStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
            styles.put(color.name(), colorStyle);
        }

        return styles;
    }

    /**
     * 获取类及其父类的所有字段
     */
    private static List<Field> getAllFields(Class<?> clazz) {
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null && clazz != Object.class) {
            for (Field field : clazz.getDeclaredFields()) {
                fieldList.add(field);
            }
            clazz = clazz.getSuperclass();
        }
        return fieldList;
    }


    //获取导出内容list->.xlsx


    //获取导入的模板
    public static <T> byte[] mbExcel(Class<?> clazz) throws IOException, IllegalAccessException {
        //Class<?> clazz = obj.getClass();
        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet("数据");
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        // 1. 获取并过滤字段（仅保留带注解且非 hidden 的字段；避免无注解字段如 serialVersionUID 出现在模板）
        List<Field> fields = getAllFields(clazz).stream()
                .filter(field -> {
                    ExcelColumn anno = field.getAnnotation(ExcelColumn.class);
                    return anno != null && !anno.hidden();
                })
                .collect(Collectors.toList());

        // 2. 创建样式（复用样式避免Excel文件过大）
        Map<String, CellStyle> styleMap = createStyles(workbook);
        CellStyle defaultStyle = styleMap.get("default");
        CellStyle nonNullWarningStyle = styleMap.get("nonNullWarning"); // 非空警告样式（黄色背景）

        // 3. 填充表头
        Row headerRow = sheet.createRow(0);
        for (int i = 0; i < fields.size(); i++) {
            Field field = fields.get(i);
            field.setAccessible(true);
            ExcelColumn anno = field.getAnnotation(ExcelColumn.class);

            // 表头名称
            String columnName = (anno != null && !anno.name().isEmpty()) ? anno.name() : field.getName();
            Cell cell = headerRow.createCell(i);
            cell.setCellValue(columnName);

            // 表头颜色（如果指定）
            if (anno != null && !anno.color().isEmpty()) {
                CellStyle colorStyle = styleMap.get(anno.color().toUpperCase());
                if (colorStyle != null) {
                    cell.setCellStyle(colorStyle);
                }
            } else {
                cell.setCellStyle(defaultStyle);
            }
            if (anno != null && anno.nonNull()) {
                cell.setCellStyle(nonNullWarningStyle);
            } else {
                cell.setCellStyle(defaultStyle);
            }

        }



        Cell tCell = headerRow.createCell(fields.size()+1);

        tCell.setCellValue("tip：黄色背景表示必填项");
        //tCell.setCellStyle(nonNullWarningStyle);
        sheet.autoSizeColumn(fields.size()+1);

        // 4. 例子
        headerRow = sheet.createRow(1);
        for (int i = 0; i < fields.size(); i++) {
            Field field = fields.get(i);
            field.setAccessible(true);
            ExcelColumn anno = field.getAnnotation(ExcelColumn.class);

            // 表头名称
            String columnName = (anno != null && !anno.value().isEmpty()) ? anno.value() : "";
            Cell cell = headerRow.createCell(i);
            cell.setCellValue(columnName);

            // 表头颜色（如果指定）
            if (anno != null && !anno.color().isEmpty()) {
                CellStyle colorStyle = styleMap.get(anno.color().toUpperCase());
                if (colorStyle != null) {
                    cell.setCellStyle(colorStyle);
                }
            } else {
                cell.setCellStyle(defaultStyle);
            }
            if (anno != null && anno.nonNull()) {
                cell.setCellStyle(nonNullWarningStyle);
            } else {
                cell.setCellStyle(defaultStyle);
            }

        }

        // 5. 设置列宽（优先使用注解指定的宽度，否则自动调整）
        for (int i = 0; i < fields.size(); i++) {
            Field field = fields.get(i);
            ExcelColumn anno = field.getAnnotation(ExcelColumn.class);
            if (anno != null && anno.columnWidth() > 0) {
                // POI列宽单位是1/256个字符宽度，这里转换为用户输入的"字符数"
                sheet.setColumnWidth(i, anno.columnWidth() * 256);
            } else {
                sheet.autoSizeColumn(i); // 自动调整
            }
        }

        // 6. 写入并返回字节数组
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        workbook.write(outputStream);
        workbook.close();
        return outputStream.toByteArray();
    }

    //解析导入的模板->.xlsx格式变为list格式

    /**
     * 导入Excel文件并转换为实体类集合
     * @param file 上传的Excel文件（.xlsx格式）
     * @param clazz 目标实体类类型
     * @return 解析后的实体类集合
     */
    public static <T> List<T> importExcel(MultipartFile file, Class<T> clazz) throws IOException, InvalidFormatException, IllegalAccessException, InstantiationException, NoSuchMethodException, ParseException {
        // 1. 验证文件
        if (file.isEmpty()) {
            throw new IllegalArgumentException("上传文件不能为空");
        }
        String fileName = file.getOriginalFilename();
        if (!fileName.endsWith(".xlsx")) {
            throw new IllegalArgumentException("请上传.xlsx格式的Excel文件");
        }

        // 2. 读取Excel文件
        try (InputStream is = file.getInputStream();
             Workbook workbook = WorkbookFactory.create(is)) { // 支持.xlsx格式
            Sheet sheet = workbook.getSheetAt(0); // 读取第一个工作表
            if (sheet == null) {
                throw new IllegalArgumentException("Excel文件中没有工作表");
            }

            // 3. 解析表头（第0行）
            Row headerRow = sheet.getRow(0);
            if (headerRow == null) {
                throw new IllegalArgumentException("Excel文件表头为空");
            }
            int headerLastCellNum = headerRow.getLastCellNum();
            List<String> headerNames = new ArrayList<>();
            for (int i = 0; i < headerLastCellNum; i++) {
                Cell cell = headerRow.getCell(i);
                headerNames.add(getCellValue(cell)); // 存储表头文字（如"用户ID"）
            }

            // 4. 建立表头与实体类字段的映射关系（根据@ExcelColumn的name匹配）
            List<Field> fields = getAllFields(clazz).stream()
                    .filter(field -> {
                        ExcelColumn anno = field.getAnnotation(ExcelColumn.class);
                        return anno != null && !anno.hidden(); // 只处理有注解且不隐藏的字段
                    })
                    .collect(Collectors.toList());

            // 表头名称 -> 字段 的映射（key：表头文字，value：对应的实体类字段）
            Map<String, Field> headerFieldMap = new HashMap<>();
            for (Field field : fields) {
                ExcelColumn anno = field.getAnnotation(ExcelColumn.class);
                String columnName = anno.name().isEmpty() ? field.getName() : anno.name(); // 注解name或字段名
                headerFieldMap.put(columnName, field);
            }

            // 5. 解析数据行（从第1行开始）
            List<T> resultList = new ArrayList<>();
            int lastRowNum = sheet.getLastRowNum();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 日期格式

            for (int rowNum = 1; rowNum <= lastRowNum; rowNum++) { // 行号从1开始（跳过表头）
                Row dataRow = sheet.getRow(rowNum);
                if (dataRow == null) continue; // 跳过空行

                // 创建实体类对象
                Constructor<T> constructor = clazz.getDeclaredConstructor();
                constructor.setAccessible(true);
                T entity = constructor.newInstance();

                // 遍历单元格，设置字段值
                for (int cellNum = 0; cellNum < headerLastCellNum; cellNum++) {
                    String headerName = headerNames.get(cellNum); // 当前列的表头名称
                    Field field = headerFieldMap.get(headerName); // 匹配对应的字段
                    if (field == null) continue; // 表头不匹配的列忽略

                    field.setAccessible(true);
                    Cell cell = dataRow.getCell(cellNum);
                    String cellValue = getCellValue(cell); // 获取单元格原始值（字符串）

                    // 非空校验（如果注解要求nonNull）
                    ExcelColumn anno = field.getAnnotation(ExcelColumn.class);
                    if (anno.nonNull() && (cellValue == null || cellValue.trim().isEmpty())) {
                        throw new IllegalArgumentException(String.format("第%d行，字段【%s】为必填项，不能为空", rowNum + 1, headerName));
                    }

                    // 针对标注为 JSON 的字符串列做宽松规范化（吸收中文引号、中文逗号、单引号）
                    if (cellValue != null && field.getType() == String.class && anno != null
                            && anno.name() != null && anno.name().toUpperCase().contains("JSON")) {
                        cellValue = normalizeJsonLikeString(cellValue);
                    }

                    // 规范化性别值："男"->1, "女"->2；支持 male/female；已是数字则保持
                    if ("gender".equals(field.getName())) {
                        cellValue = normalizeGender(cellValue);
                    }

                    // 类型转换并设置字段值
                    Object value = convertValue(cellValue, field.getType(), sdf);
                    if (value != null) {
                        field.set(entity, value);
                    }
                }

                // 应用通用默认值（如 createTime/updateTime/status 等）
                applyCommonDefaults(entity);

                resultList.add(entity);
            }

            log.info("解析Excel文件成功，共{}行数据", resultList.size());
            log.info("实体类：{}", resultList);

            return resultList;
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 获取Excel单元格的字符串值（兼容各种单元格类型）
     */
    private static String getCellValue(Cell cell) {
        if (cell == null) return null;

        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue().trim();
            case NUMERIC:
                // 处理日期格式（Excel日期本质是数字）
                if (DateUtil.isCellDateFormatted(cell)) {
                    return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(cell.getDateCellValue());
                } else {
                    // 数字转字符串（避免科学计数法）
                    return String.valueOf(cell.getNumericCellValue()).replaceAll("\\.0$", "");
                }
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            case FORMULA:
                return cell.getCellFormula(); // 公式单元格直接返回公式（也可计算结果）
            default:
                return null;
        }
    }


    /**
     * 将单元格字符串值转换为目标字段类型
     */
    private static Object convertValue(String cellValue, Class<?> targetType, SimpleDateFormat sdf) throws ParseException {
        if (cellValue == null || cellValue.trim().isEmpty()) {
            return null;
        }
        String value = cellValue.trim();

        // 转换为对应类型
        if (targetType == String.class) {
            return value;
        } else if (targetType == Integer.class || targetType == int.class) {
            return Integer.parseInt(value);
        } else if (targetType == Long.class || targetType == long.class) {
            return Long.parseLong(value);
        } else if (targetType == Double.class || targetType == double.class) {
            return Double.parseDouble(value);
        } else if (targetType == Float.class || targetType == float.class) {
            return Float.parseFloat(value);
        } else if (targetType == Boolean.class || targetType == boolean.class) {
            return Boolean.parseBoolean(value);
        } else if (targetType == Date.class) {
            // 更加宽松的日期解析，兼容斜杠/短横线/仅日期/Excel序列号等
            return parseDateFlexible(value);
        } else if (targetType == LocalDate.class) {
            return parseLocalDate(value);
        } else if (targetType == LocalDateTime.class) {
            return parseLocalDateTime(value);
        } else {
            throw new IllegalArgumentException("不支持的字段类型：" + targetType.getName());
        }
    }

    /**
     * 宽松解析 Date：
     * - 支持 "yyyy-MM-dd HH:mm:ss"、"yyyy/MM/dd HH:mm:ss"、"yyyy-M-d H:m:s"、"yyyy/M/d H:m:s"
     * - 支持 "yyyy-MM-dd"、"yyyy/MM/dd" 等仅日期
     * - 支持 ISO 风格 "yyyy-MM-dd'T'HH:mm:ss"
     * - 支持 Excel 日期序列号（数字字符串，如 45245 或 45245.5）
     */
    private static Date parseDateFlexible(String value) throws ParseException {
        String v = value == null ? null : value.trim();
        if (v == null || v.isEmpty()) {
            return null;
        }

        // 数字字符串：尝试按 Excel 序列号解析
        if (v.matches("^\\d+(\\.\\d+)?$")) {
            try {
                double serial = Double.parseDouble(v);
                return DateUtil.getJavaDate(serial);
            } catch (Exception ignored) { }
        }

        String[] patterns = new String[] {
                "yyyy-MM-dd HH:mm:ss",
                "yyyy/MM/dd HH:mm:ss",
                "yyyy-M-d H:m:s",
                "yyyy/M/d H:m:s",
                "yyyy-MM-dd'T'HH:mm:ss",
                "yyyy-MM-dd",
                "yyyy/MM/dd",
                "yyyy-M-d",
                "yyyy/M/d"
        };
        for (String p : patterns) {
            try {
                SimpleDateFormat fmt = new SimpleDateFormat(p);
                fmt.setLenient(false);
                return fmt.parse(v);
            } catch (java.text.ParseException ignored) { }
        }

        // 兜底：截取日期部分并将斜杠替换为短横线
        if (v.length() >= 10) {
            String d = v.substring(0, 10).replace('/', '-');
            try {
                SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
                fmt.setLenient(false);
                return fmt.parse(d);
            } catch (java.text.ParseException ignored) { }
        }

        throw new ParseException("无法解析日期: " + value + "，支持格式：yyyy-MM-dd HH:mm:ss、yyyy/MM/dd HH:mm:ss、yyyy-MM-dd、yyyy/MM/dd", 0);
    }

    /**
     * 将形如 ['张三','李四']、["张三", "李四"]、【张三，李四】 等不严格 JSON 的字符串
     * 规范化为合法 JSON 字符串，便于后端按 String 入库或供前端解析。
     */
    private static String normalizeJsonLikeString(String raw) {
        if (raw == null) return null;
        String s = raw.trim();
        // 替换中文括号/引号/逗号
        s = s.replace('【', '[').replace('】', ']');
        s = s.replace('“', '"').replace('”', '"');
        s = s.replace('‘', '\'').replace('’', '\'');
        s = s.replace('，', ',');
        // 如果是单引号包裹的项，替换为双引号
        // 仅在数组上下文中做宽松处理
        if (s.startsWith("[") && s.endsWith("]")) {
            // 将 ['张三','李四'] 转换为 ["张三","李四"]
            String inner = s.substring(1, s.length() - 1);
            // 去除多余空白后替换单引号为双引号
            inner = inner.trim().replace('\'', '"');
            // 合成
            s = "[" + inner + "]";
        }
        return s;
    }

    /**
     * 规范化性别：男→1，女→2；male→1，female→2；数字保留。
     */
    private static String normalizeGender(String raw) {
        if (raw == null) return null;
        String v = raw.trim();
        if (v.isEmpty()) return v;
        String lower = v.toLowerCase(Locale.ROOT);
        if ("男".equals(v) || "male".equals(lower) || "m".equals(lower)) return "1";
        if ("女".equals(v) || "female".equals(lower) || "f".equals(lower)) return "2";
        // 其余返回原值，允许使用 1/2 等
        return v;
    }

    /**
     * 为常见审计/运营字段赋默认值，避免 NOT NULL 约束导致导入失败。
     * 针对存在这些字段的实体生效，其余实体忽略。
     */
    private static <T> void applyCommonDefaults(T entity) {
        try {
            // createTime / updateTime -> now
            LocalDateTime now = LocalDateTime.now();
            setIfNull(entity, "createTime", now);
            setIfNull(entity, "updateTime", now);

            // status 默认启用 1
            setIfNull(entity, "status", 1);

            // 常见计数/排序类默认 0
            setIfNull(entity, "sortOrder", 0);
            setIfNull(entity, "downloadCount", 0);
            setIfNull(entity, "citationCount", 0);
            setIfNull(entity, "viewCount", 0);
            setIfNull(entity, "progressPercentage", 0);
        } catch (Exception ignored) { }
    }

    private static <T> void setIfNull(T entity, String fieldName, Object value) throws IllegalAccessException {
        try {
            Field f = getDeclaredField(entity.getClass(), fieldName);
            if (f == null) return;
            f.setAccessible(true);
            Object current = f.get(entity);
            if (current == null) {
                // 类型适配：Date/LocalDateTime
                if (f.getType() == Date.class && value instanceof LocalDateTime ldt) {
                    f.set(entity, java.sql.Timestamp.valueOf(ldt));
                } else if (f.getType() == LocalDateTime.class && value instanceof LocalDateTime ldt2) {
                    f.set(entity, ldt2);
                } else if ((f.getType() == Integer.class || f.getType() == int.class) && value instanceof Integer i) {
                    f.set(entity, i);
                } else {
                    f.set(entity, value);
                }
            }
        } catch (Exception ignored) { }
    }

    private static Field getDeclaredField(Class<?> clazz, String name) {
        Class<?> c = clazz;
        while (c != null && c != Object.class) {
            try {
                return c.getDeclaredField(name);
            } catch (NoSuchFieldException ignored) { }
            c = c.getSuperclass();
        }
        return null;
    }

    private static LocalDate parseLocalDate(String value) throws ParseException {
        // 支持：yyyy-MM-dd、yyyy/MM/dd、yyyy-M-d、yyyy/M/d、以及携带时间的格式（取日期部分）
        String[] patterns = new String[]{
                "yyyy-MM-dd",
                "yyyy/M/d",
                "yyyy/MM/dd",
                "yyyy-MM-dd HH:mm:ss",
                "yyyy/M/d H:m:s",
                "yyyy/MM/dd HH:mm:ss"
        };
        for (String p : patterns) {
            try {
                if (p.contains("H")) {
                    return LocalDateTime.parse(value, DateTimeFormatter.ofPattern(p)).toLocalDate();
                } else {
                    return LocalDate.parse(value, DateTimeFormatter.ofPattern(p));
                }
            } catch (DateTimeParseException ignored) { }
        }
        // 兜底：仅日期数字（如 2024-06-01 00:00:00 去掉时间）
        if (value.length() >= 10) {
            String d = value.substring(0, 10).replace('/', '-');
            try {
                return LocalDate.parse(d, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            } catch (DateTimeParseException ignored) { }
        }
        throw new ParseException("无法解析日期: " + value, 0);
    }

    private static LocalDateTime parseLocalDateTime(String value) throws ParseException {
        String[] patterns = new String[]{
                "yyyy-MM-dd HH:mm:ss",
                "yyyy/M/d H:m:s",
                "yyyy/MM/dd HH:mm:ss",
                "yyyy-MM-dd'T'HH:mm:ss"
        };
        for (String p : patterns) {
            try {
                return LocalDateTime.parse(value, DateTimeFormatter.ofPattern(p));
            } catch (DateTimeParseException ignored) { }
        }
        // 如果只给了日期，默认补 00:00:00
        try {
            LocalDate d = parseLocalDate(value);
            return d.atStartOfDay();
        } catch (Exception ignored) { }
        throw new ParseException("无法解析日期时间: " + value, 0);
    }

}
